'use client';

import type { TBody, TMetadata } from '@/types';
import { NavbarPage } from '@/app/[locale]/navbar';
import FooterPage from '@/app/[locale]/footer';
import { type ReactNode, useEffect, useState } from 'react';
import classNames from 'classnames';
import type { IClientMessage, IPagination, IQueryParams } from '@/interfaces';
import { useInfiniteQuery, useMutation } from '@tanstack/react-query';
import useToast from '@/hooks/useToast';
import {
  markMessageRead,
  queryAllMessage,
  removeMessage,
} from '@/services/api';
import { toRelativeTime } from '@/lib/tool';
import GeneralMessage from '@/app/[locale]/message/general';
import ErrorPage from '@/app/[locale]/error/error';
import LoadPage from '@/app/[locale]/load/load';
import Nodata from '@/app/[locale]/common/nodata/nodata';
import useUser from '@/hooks/useUser';
import { useTranslations } from 'use-intl';

export default function MessagePage({ metadata }: { metadata: TMetadata }) {
  return (
    <>
      <NavbarPage metadata={metadata} />
      <Message metadata={metadata} />
      <FooterPage metadata={metadata} />
    </>
  );
}

const Message = ({ metadata }: { metadata: TMetadata }) => {
  const t = useTranslations('MessagePage');
  const [currentReadMessageId, setCurrentReadMessageId] = useState<number>();
  const [currentRemoveMessageId, setCurrentRemoveMessageId] =
    useState<number>();
  const userQuery = useUser(metadata);
  const { show } = useToast();
  const [pages, setPages] = useState<IClientMessage[]>(
    (metadata.all['message'].v as IPagination<IClientMessage>).content,
  );

  const queryAllMessageQuery = useInfiniteQuery(
    metadata.all['message'].k,
    async (context) => {
      return (await queryAllMessage({
        query: {
          ...(context.queryKey[1] as IQueryParams),
          ...context.pageParam,
        },
      })) as IPagination<IClientMessage>;
    },
    {
      enabled: !!(userQuery.data && userQuery.data.user),
      keepPreviousData: true,
      getPreviousPageParam: (firstPage) => {
        const pageable = firstPage.pageable;
        if (pageable.previous) {
          return {
            page: Math.max(pageable.page - 1, 0),
          };
        }
      },
      getNextPageParam: (lastPage) => {
        const pageable = lastPage.pageable;
        if (pageable.next) {
          return {
            page: Math.min(pageable.page + 1, pageable.pages),
          };
        }
      },
      initialData: () => {
        if (metadata.all['message']) {
          return {
            pages: [metadata.all['message'].v as IPagination<IClientMessage>],
            pageParams: [
              {
                page: Math.max(
                  ((metadata.all['message'].k[1] as IQueryParams).page ?? 1) -
                    1,
                  0,
                ),
              },
            ],
          };
        }
      },
    },
  );

  const clientMarkMessageReadMutation = useMutation(
    async (variables: TBody<void>) => {
      await markMessageRead(variables);
    },
  );
  const clientRemoveMessageMutation = useMutation(
    async (variables: TBody<void>) => {
      await removeMessage(variables);
    },
  );

  useEffect(() => {
    if (queryAllMessageQuery.data) {
      setPages(
        queryAllMessageQuery.data.pages
          .flatMap((item) => item.content)
          .map((item) => {
            item._createdOnText = toRelativeTime(item.createdOn);
            return item;
          }),
      );
    }
  }, [queryAllMessageQuery.data]);

  async function onClickRead(item: IClientMessage, index: number) {
    try {
      setCurrentReadMessageId(item.id);
      const { id, state } = item;
      if ('state' in item && state !== 'UNREAD') {
        return;
      }

      await clientMarkMessageReadMutation.mutateAsync({
        id,
      });

      setPages((prevState) => {
        prevState[index].state = 'HAVE_READ';
        return prevState;
      });

      show({
        type: 'SUCCESS',
        message: t('isReadComplete'),
      });
    } catch (e) {
      clientMarkMessageReadMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    } finally {
      setCurrentReadMessageId(undefined);
    }
  }

  async function onClickRemove(item: IClientMessage, index: number) {
    try {
      setCurrentRemoveMessageId(item.id);
      await clientRemoveMessageMutation.mutateAsync({
        id: item.id,
      });

      if (index === pages.length - 1) {
        await queryAllMessageQuery.refetch({ throwOnError: true });

        show({
          type: 'PRIMARY',
          message: t('isNextPageLoading'),
        });
        return;
      }

      setPages((prevState) => {
        prevState.splice(index, 1);
        return prevState;
      });

      show({
        type: 'SUCCESS',
        message: t('isDeleteComplete'),
      });
    } catch (e) {
      clientRemoveMessageMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    } finally {
      setCurrentRemoveMessageId(undefined);
    }
  }

  async function onClickLoadMore() {
    try {
      await queryAllMessageQuery.fetchNextPage();
    } catch (e) {
      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  if (queryAllMessageQuery.data) {
    if (pages.length > 0) {
      return (
        <div className="col">
          <div className="container py-4">
            <div className="row">
              <div className="col-2"></div>
              <div className="col">
                {pages.map((item, index) => {
                  return (
                    <div
                      key={item.id}
                      className="row z-1"
                      style={{ marginBottom: -2 }}
                    >
                      <div className="col-auto px-0 mx-0">
                        <div className="d-flex flex-column align-items-center h-100">
                          <Circle className="yw-bg">
                            <Circle width={32} height={32} className="yw-bg">
                              <i
                                className={classNames(
                                  'bi bi-envelope-fill fs-5',
                                  item.state === 'UNREAD'
                                    ? 'text-primary'
                                    : 'text-secondary',
                                )}
                              ></i>
                            </Circle>
                          </Circle>
                          <Line className="yw-bg" />
                        </div>
                      </div>
                      <div className="col-auto px-0 mx-0">
                        <Line reverse className="yw-bg" />
                      </div>
                      <div className="col p-2 pb-3">
                        <div className="card border-0">
                          <GeneralMessage item={item} />

                          <div className="card-footer">
                            <div className="d-flex align-items-center justify-content-between">
                              <div>{item._createdOnText}</div>
                              <div className="hstack gap-2">
                                <button
                                  disabled={currentRemoveMessageId === item.id}
                                  onClick={onClickRemove.bind(
                                    this,
                                    item,
                                    index,
                                  )}
                                  type="button"
                                  className="btn focus-ring focus-ring-danger text-danger btn-sm"
                                >
                                  {currentRemoveMessageId === item.id && (
                                    <span
                                      className="spinner-border spinner-border-sm me-2"
                                      role="status"
                                      aria-hidden="true"
                                    ></span>
                                  )}
                                  {t('deleteMessage')}
                                </button>

                                {item.state === 'UNREAD' && (
                                  <button
                                    disabled={currentReadMessageId === item.id}
                                    onClick={onClickRead.bind(
                                      this,
                                      item,
                                      index,
                                    )}
                                    type="button"
                                    className="btn focus-ring focus-ring-primary text-primary btn-sm"
                                  >
                                    {currentReadMessageId === item.id && (
                                      <span
                                        className="spinner-border spinner-border-sm me-2"
                                        role="status"
                                        aria-hidden="true"
                                      ></span>
                                    )}
                                    {t('isRead')}
                                  </button>
                                )}
                              </div>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                  );
                })}

                <div className="my-4">
                  <LoadMoreBtn
                    onClickLoadMore={onClickLoadMore}
                    isDisabled={
                      !queryAllMessageQuery.hasNextPage ||
                      queryAllMessageQuery.isFetchingNextPage
                    }
                    isFetchingNextPage={queryAllMessageQuery.isFetchingNextPage}
                  />
                </div>
              </div>
              <div className="col-2"></div>
            </div>
          </div>
        </div>
      );
    }

    return (
      <div className="col">
        <div className="container py-4">
          <Nodata />
        </div>
      </div>
    );
  }

  if (queryAllMessageQuery.error) {
    return <ErrorPage error={queryAllMessageQuery.error} />;
  }

  return <LoadPage />;
};

const Circle = ({
  width,
  height,
  children,
  className,
}: {
  width?: number;
  height?: number;
  children?: ReactNode;
  className?: string;
}) => {
  return (
    <div
      className={classNames(
        'rounded-circle border d-flex align-items-center justify-content-center flex-shrink-0',
        className,
      )}
      style={{ width: width ?? 48, height: height ?? 48 }}
    >
      {children && children}
    </div>
  );
};

const Line = ({
  width,
  height,
  className,
  reverse,
}: {
  width?: number;
  height?: number;
  className?: string;
  reverse?: boolean;
}) => {
  return (
    <div
      className={classNames(
        'border',
        className,
        reverse
          ? 'w-100 border-top border-bottom border-start-0 border-end-0'
          : 'h-100 border-start border-end border-top-0 border-bottom-0',
      )}
      style={
        reverse
          ? {
              width: width && width > 0 ? width + 2 : '12px !important',
              height: height ?? 10,
              marginLeft: -2,
              marginTop: '1.2rem',
            }
          : {
              width: width ?? 10,
              marginTop: -2,
            }
      }
    ></div>
  );
};

const LoadMoreBtn = ({
  onClickLoadMore,
  isDisabled,
  isFetchingNextPage,
}: {
  onClickLoadMore: () => void;
  isDisabled: boolean;
  isFetchingNextPage: boolean;
}) => {
  return (
    <button
      onClick={onClickLoadMore}
      disabled={isFetchingNextPage && isDisabled}
      type="button"
      className="btn rounded-pill w-100"
    >
      {isFetchingNextPage ? (
        <>
          <span
            className="spinner-border spinner-border-sm me-2"
            role="status"
            aria-hidden="true"
          ></span>
          Loading...
        </>
      ) : (
        <i className="bi bi-three-dots"></i>
      )}
    </button>
  );
};
